﻿using System;
using System.Web.Mvc;
using System.Web.Routing;
using SnippetBox.Core.Common.Extensions;

namespace SnippetBox.Core.Common.Mvc.Routes {
    public class LowercaseRoute : Route {
        public LowercaseRoute(string url, IRouteHandler routeHandler)
            : base(url, routeHandler) {}

        public LowercaseRoute(string url, RouteValueDictionary defaults, IRouteHandler routeHandler)
            : base(url, defaults, routeHandler) {}

        public LowercaseRoute(string url, RouteValueDictionary defaults, RouteValueDictionary constraints,
                              IRouteHandler routeHandler)
            : base(url, defaults, constraints, routeHandler) {}

        public LowercaseRoute(string url, RouteValueDictionary defaults, RouteValueDictionary constraints,
                              RouteValueDictionary dataTokens, IRouteHandler routeHandler)
            : base(url, defaults, constraints, dataTokens, routeHandler) {}

        public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values) {
            VirtualPathData path = base.GetVirtualPath(requestContext, values);

            if (path != null)
                path.VirtualPath = path.VirtualPath.ToLowerInvariant();

            return path;
        }
    }

    public static class RouteCollectionExtensions {
        public static LowercaseRoute MapRouteLowercase(this RouteCollection routes, string name, string url) {
            return MapRouteLowercase(routes, name, url, null /* defaults */, null /* constraints */, null);
        }

        public static LowercaseRoute MapRouteLowercase(this RouteCollection routes, string name, string url, object defaults) {
            return MapRouteLowercase(routes, name, url, defaults, null /* constraints */, null);
        }

        public static LowercaseRoute MapRouteLowercase(this RouteCollection routes, string name, string url, object defaults, object constraints) {
            return MapRouteLowercase(routes, name, url, defaults, constraints, null /* namespaces */);
        }

        public static LowercaseRoute MapRouteLowercase(this RouteCollection routes, string name, string url, string[] namespaces) {
            return MapRouteLowercase(routes, name, url, null /* defaults */, null /* constraints */, namespaces);
        }

        public static LowercaseRoute MapRouteLowercase(this RouteCollection routes, string name, string url, object defaults, string[] namespaces) {
            return MapRouteLowercase(routes, name, url, defaults, null /* constraints */, namespaces);
        }

        public static LowercaseRoute MapRouteLowercase(this RouteCollection routes, string name, string url, object defaults, object constraints, string[] namespaces) {
            if (routes == null)
                throw new ArgumentNullException("routes");

            if (url == null)
                throw new ArgumentNullException("url");

            LowercaseRoute route = new LowercaseRoute(url, new MvcRouteHandler()) {
                Defaults = new RouteValueDictionary(defaults),
                Constraints = new RouteValueDictionary(constraints),
                DataTokens = new RouteValueDictionary()
            };

            if ((namespaces != null) && (namespaces.Length > 0)) {
                route.DataTokens["Namespaces"] = namespaces;
            }

            routes.Add(name, route);

            return route;
        }

    }
    public static class AreaRegistrationExtensions {
        public static Route MapRouteLowercase(this AreaRegistrationContext context, string name, string url) {
            return MapRouteLowercase(context, name, url, (object)null /* defaults */);
        }

        public static Route MapRouteLowercase(this AreaRegistrationContext context, string name, string url, object defaults) {
            return MapRouteLowercase(context, name, url, defaults, (object)null /* constraints */);
        }

        public static Route MapRouteLowercase(this AreaRegistrationContext context, string name, string url, object defaults, object constraints) {
            return MapRouteLowercase(context, name, url, defaults, constraints, null /* namespaces */);
        }

        public static Route MapRouteLowercase(this AreaRegistrationContext context, string name, string url, string[] namespaces) {
            return MapRouteLowercase(context, name, url, (object)null /* defaults */, namespaces);
        }

        public static Route MapRouteLowercase(this AreaRegistrationContext context, string name, string url, object defaults, string[] namespaces) {
            return MapRouteLowercase(context, name, url, defaults, null /* constraints */, namespaces);
        } 
        public static LowercaseRoute MapRouteLowercase(this AreaRegistrationContext context, string name, string url, object defaults, object constraints, string[] namespaces) {
            LowercaseRoute route = context.Routes.MapRouteLowercase(name, url, defaults, constraints, namespaces);
            route.DataTokens["area"] = context.AreaName; 

            // disabling the namespace lookup fallback mechanism keeps this areas from accidentally picking up
            // controllers belonging to other areas
            bool useNamespaceFallback = (namespaces == null || namespaces.Length == 0); 
            route.DataTokens["UseNamespaceFallback"] = useNamespaceFallback;
 
            return route; 
        }
    }

}